package com.king.springframework.beans.factory.xml;

import cn.hutool.core.util.StrUtil;
import com.king.springframework.beans.BeanException;
import com.king.springframework.beans.PropertyValue;
import com.king.springframework.beans.factory.config.BeanDefinition;
import com.king.springframework.beans.factory.config.BeanReference;
import com.king.springframework.beans.factory.support.AbstractBeanDefinitionReader;
import com.king.springframework.beans.factory.support.BeanDefinitionReader;
import com.king.springframework.beans.factory.support.BeanDefinitionRegistry;
import com.king.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import com.king.springframework.core.io.Resource;
import com.king.springframework.core.io.ResourceLoader;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.dom4j.util.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class XmlBeanDefinitionReader extends AbstractBeanDefinitionReader {
    public XmlBeanDefinitionReader(BeanDefinitionRegistry beanDefinitionRegistry, ResourceLoader resourceLoader) {
        super(beanDefinitionRegistry, resourceLoader);
    }

    public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
        super(registry);
    }

    @Override
    public void loadBeanDefinitions(Resource resource) throws BeanException {
        try {
            InputStream inputStream = resource.getInputStream();
            doLoadBeanDefinitions(inputStream);
        } catch (IOException | DocumentException | ClassNotFoundException e) {
            throw new BeanException("IOException parsing XML document from " + resource, e);
        }
    }

    @Override
    public void loadBeanDefinitions(Resource... resources) throws BeanException {
        for(Resource resource:resources){
            loadBeanDefinitions(resource);
        }
    }

    @Override
    public void loadBeanDefinitions(String location) throws BeanException {
        ResourceLoader resourceLoader = getResourceLoader();
        Resource resource = resourceLoader.getResource(location);
        loadBeanDefinitions(resource);
    }

    @Override
    public void loadBeanDefinitions(String... locations) throws BeanException {
        ResourceLoader resourceLoader = getResourceLoader();
        for(String loccation:locations){
            Resource resource = resourceLoader.getResource(loccation);
            loadBeanDefinitions(resource);
        }
    }
    protected  void doLoadBeanDefinitions(InputStream inputStream) throws DocumentException, ClassNotFoundException {
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(inputStream);
        Element root = document.getRootElement();
        // 解析 context:component-scan 标签，扫描包中的类并提取相关信息，用于组装 BeanDefinition
        Element componentScan = root.element("component-scan");
        if(null != componentScan){
            String scanPath = componentScan.attributeValue("base-package");
            if(StrUtil.isEmpty(scanPath)){
                throw new BeanException("The value of base-package attribute can not be empty or null");
            }
            scanPages(scanPath);
        }
        List<Element> beanList = root.elements("bean");
        for(Element bean:beanList){
            String id = bean.attributeValue("id");
            String name = bean.attributeValue("name");
            String className = bean.attributeValue("class");
            String initMethod = bean.attributeValue("init-method");
            String destroyMethodName = bean.attributeValue("destroy-method");
            String beanScope = bean.attributeValue("scope");
            // 获取 Class，方便获取类中的名称
            Class<?> clazz = Class.forName(className);
            //优先级id > name
            String beanName = StrUtil.isNotEmpty(id)?id:className;
            if (StrUtil.isEmpty(beanName)) {
                beanName = StrUtil.lowerFirst(clazz.getSimpleName());
            }
            // 定义Bean
            BeanDefinition beanDefinition = new BeanDefinition(clazz);
            beanDefinition.setInitMethodName(initMethod);
            beanDefinition.setDestoryMethodName(destroyMethodName);
            if(StrUtil.isNotEmpty(beanScope)){
                beanDefinition.setScope(beanScope);
            }
            //填充Bean属性
            List<Element> propertyList = bean.elements("property");
            for(Element property:propertyList){
                //解析标签property
                String attrName = property.attributeValue("name");
                String attrValue = property.attributeValue("value");
                String attrRef = property.attributeValue("ref");
                Object value = StrUtil.isNotEmpty(attrRef)?new BeanReference(attrRef):attrValue;
                PropertyValue propertyValue = new PropertyValue(attrName,attrValue);
                beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
                if (getRegistry().containsBeanDefinition(beanName)) {
                    throw new BeanException("Duplicate beanName[" + beanName + "] is not allowed");
                }
                // 注册 BeanDefinition
                getRegistry().registerBeanDefinition(beanName, beanDefinition);
            }

        }

    }

    private void scanPages(String scanPath){
        String[] basePackages = StrUtil.splitToArray(scanPath,',');
        ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(getRegistry());
        scanner.doScan(basePackages);
    }
}
